package cmd

import (
	"bytes"
	"html/template"
	"net/http"
	_ "net/http/pprof"
	"os"

	"github.com/gin-gonic/gin"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/spf13/cobra"
	ginprometheus "github.com/zsais/go-gin-prometheus"
)

// 基础信息
type Base struct {
	Status int
	Error  string
}

// 产品信息
type Product struct {
	Title           string
	DescriptionHtml string
}

// 图书详情信息
type Details struct {
	Base
	ISBN10    string
	Publisher string
	Pages     int
	Type      string
	Language  string
}

// 评星
type Rating struct {
	Base
	Stars int    // 星级
	Color string // 颜色
}

// 一条评论
type Review struct {
	Rating   Rating // 评星
	Text     string // 评论内容
	Reviewer string // 评论者
}

// 评论信息
type Reviews struct {
	Base
	Reviews     []Review
	PodName     string
	ClusterName string
}

// 产品页面数据
type ProductPage struct {
	User    string
	Product Product
	Details Details
	Reviews Reviews
}

// 定义一个命令，其中的 Run 函数会在执行命令时被 cobra 调用
var serveCmd = &cobra.Command{
	Use:   "serve", // `go run . serve`
	Short: "启动服务器",
	Long:  `start bookinfo server.`,
	Run: func(cmd *cobra.Command, args []string) {
		r := gin.Default()

		// Gin 服务的 Prometheus 指标
		// 通过 /metrics 接口获取
		p := ginprometheus.NewPrometheus("gin")
		p.Use(r)

		// 静态资源
		r.Static("/static", "static")

		// 首页
		r.GET("/", func(c *gin.Context) {
			data, _ := os.ReadFile("template/index.html")
			c.Data(http.StatusOK, "text/html; charset=utf-8", data)
		})

		// 图书详情页
		r.GET("/productpage", func(c *gin.Context) {
			data, _ := os.ReadFile("template/productpage.html")
			// 供模板使用的工具函数
			funcs := template.FuncMap{
				// 将字符串解析为 Html
				"html_format": func(s string) template.HTML {
					return template.HTML(s)
				},
				// 整数转换为数组
				"inRange": func(count int) []int {
					return make([]int, count)
				},
				// 两个整数相减
				"sub": func(a, b int) int {
					return a - b
				},
			}
			tmpl, err := template.New("productpage").Funcs(funcs).Parse(string(data))
			if err != nil {
				c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte(err.Error()))
				return
			}
			// 模板的动态数据
			product_page := ProductPage{
				User: "admin",
				Product: Product{
					Title:           "The Comedy of Errors",
					DescriptionHtml: `<a href="https://en.wikipedia.org/wiki/The_Comedy_of_Errors" target="_blank">Learn more about the book</a>`,
				},
				Details: Details{
					Base:      Base{Status: 200, Error: ""},
					ISBN10:    "1234567890",
					Publisher: "The Comedy of Errors",
					Pages:     200,
					Type:      "Hardcover",
					Language:  "English",
				},
				Reviews: Reviews{
					Base: Base{Status: 200, Error: ""},
					Reviews: []Review{
						{
							Rating: Rating{
								Base:  Base{Status: 200, Error: ""},
								Stars: 5, Color: "yellow",
							},
							Text:     "This is a review",
							Reviewer: "admin",
						},
						{
							Rating: Rating{
								Base:  Base{Status: 200, Error: ""},
								Stars: 4, Color: "yellow",
							},
							Text:     "This is a review",
							Reviewer: "admin",
						},
					},
					PodName:     "bookinfo-1",
					ClusterName: "default",
				},
			}
			buf := bytes.Buffer{}
			err = tmpl.Execute(&buf, product_page)
			if err != nil {
				c.Data(http.StatusInternalServerError, "text/html; charset=utf-8", []byte(err.Error()))
				return
			}
			c.Data(http.StatusOK, "text/html; charset=utf-8", buf.Bytes())
		})

		// pprof 和 metrics
		// 需要导入 "net/http/pprof"
		go func() {
			// http://192.168.3.21:9060/debug/pprof/
			// http://192.168.3.21:9060/metrics
			http.Handle("/metrics", promhttp.Handler())
			http.ListenAndServe(":9060", nil)
		}()

		r.Run(":9061")
	},
}

func init() {
	rootCmd.AddCommand(serveCmd)

	// Here you will define your flags and configuration settings.

	// Cobra supports Persistent Flags which will work for this command
	// and all subcommands, e.g.:
	// serveCmd.PersistentFlags().String("foo", "", "A help for foo")

	// Cobra supports local flags which will only run when this command
	// is called directly, e.g.:
	// serveCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle")
}
